##########################################
## Copyright 2012-2013 Ceruti Francesco & contributors
##
## This file is part of LiSP (Linux Show Player).
##########################################

from PyQt4 import QtCore, QtGui

from lisp.core.plugin import Plugin
from lisp.core.timing import MediaTime
from lisp.core.media import Media

from .p_actions import Pause, Play, Seek, Stop, ToSpeed, ToVolume, PAction


class Programming(Plugin):

    PLUGIN_NAME = "Programming"

    SYNTAX = {Pause.ID: Pause,
              Play.ID: Play,
              Seek.ID: Seek,
              Stop.ID: Stop,
              ToSpeed.ID: ToSpeed,
              ToVolume.ID: ToVolume
              }

    def __init__(self, main):
        self.collector = main.collector
        self.main = main

        self.eventProgrammingAction = QtGui.QAction(None, triggered=lambda: self.showUI(True))
        self.eventProgrammingAction.setText("Event programming")
        self.stepProgrammingAction = QtGui.QAction(None, triggered=lambda: self.showUI(False))
        self.stepProgrammingAction.setText("Step programming")

        self.programs = []
        self.reset()

    def showUI(self, timeMode):
        contextMedia = self.layout.contextMenuMedia()
        if(contextMedia.conf != Media.NONE):
            pUI = ProgrammingUI(contextMedia, timeMode)
            if(timeMode and contextMedia in self.media):
                pUI.setActions(self.programs[self.media.index(contextMedia)].actions)
            elif(not timeMode):
                pUI.setActions(self.layout.mediaActionsList(contextMedia))
            pUI.exec_()

            if(pUI.result() == pUI.Accepted):
                program = pUI.getProgramStr()
                if(timeMode):
                    contextMedia.conf['eprogramming'] = program
                    if(contextMedia in self.media):
                        if(program != ''):
                            self.programs[self.media.index(contextMedia)].setActions(pUI.getActions())
                        else:
                            self.programs.pop(self.media.index(contextMedia)).destroy()
                            self.media.remove(contextMedia)
                    elif(program != ''):
                        p = Program(pUI.getActions(), contextMedia)
                        self.programs.append(p)
                        self.media.append(contextMedia)
                else:
                    contextMedia.conf['sprogramming'] = program
                    if(program != ''):
                        self.layout.setMediaActionsList(contextMedia, pUI.getActions())
                    else:
                        contextMedia.conf.pop('sprogramming')

    def reload(self):

        for media in self.collector.media:
            if(media.state != Media.NONE):
                if('eprogramming' in media.conf):
                    if(media in self.media):
                        self.programs[media].setActions(self.parseEvent(media))
                    else:
                        p = Program(self.parseEvent(media), media)
                        self.programs.append(p)
                        self.media.append(media)
                if('sprogramming' in media.conf):
                    self.layout.setMediaActionsList(media, self.parseStep(media))

    def reset(self):
        self.layout = self.main.collectorUi
        self.layout.registerContextAction(self.eventProgrammingAction, True)
        self.layout.registerContextAction(self.stepProgrammingAction, False, True)

        for program in self.programs:
            program.destroy()

        self.media = []
        self.programs = []

    def parseStep(self, media):
        program = media.conf['sprogramming'].split(';')
        return self.parse(program, media)

    def parseEvent(self, media):
        program = media.conf['eprogramming'].split(';')
        return sorted(self.parse(program, media), key=PAction.getTime)

    def parse(self, program, media):
        actions = []
        for p in program:
            action = Programming.SYNTAX[p.split(':')[1]](media)
            action.fromString(p)
            actions.append(action)
        return actions


class Row(QtGui.QWidget):

    def __init__(self, media, timeMode, parent=None):
        super(Row, self).__init__(parent)

        self.actionsClasses = [Pause, Seek, ToVolume]
        self.actionsClasses.append(Stop if timeMode else Play)
        self.actionsClasses.sort(key=lambda o: o.ID)
        self.actionsStr = [action.ID for action in self.actionsClasses]

        self.media = media
        self.action = self.actionsClasses[0](media)

        self.hLayout = QtGui.QHBoxLayout(self)
        self.hLayout.setMargin(2)

        self.timeEdit = QtGui.QTimeEdit(self)
        self.timeEdit.setDisplayFormat('mm.ss')
        self.timeEdit.setMinimumTime(QtCore.QTime(0, 0, 0))
        self.timeEdit.setMaximumTime(QtCore.QTime(0, (media.conf['duration'] / 60000) % 60, (media.conf['duration'] / 1000) % 60))

        if(timeMode):
            def onTimeChanged(time):
                self.action.time = (time.minute() * 60 + time.second()) * 1000
            self.timeEdit.timeChanged.connect(onTimeChanged)
        else:
            self.timeEdit.setEnabled(False)

        self.hLayout.addWidget(self.timeEdit)

        self.comboBox = QtGui.QComboBox(self)
        self.comboBox.addItems(self.actionsStr)
        self.comboBox.currentIndexChanged.connect(self.onActionChanged)

        self.hLayout.addWidget(self.comboBox)

        self.settingsButton = QtGui.QPushButton('Settings', self)
        self.settingsButton.clicked.connect(lambda: self.action.showSettingsUi())
        self.settingsButton.setEnabled(self.action.hasSettingsUi)

        self.hLayout.addWidget(self.settingsButton)

    def onActionChanged(self, index):
        self.action = self.actionsClasses[index](self.media)
        time = (self.timeEdit.time().minute() * 60 + self.timeEdit.time().second()) * 1000
        self.action.time = time if time > 0 else 1
        self.settingsButton.setEnabled(self.action.hasSettingsUi)

    def setAction(self, action):
        self.timeEdit.setTime(QtCore.QTime(0, (action.time / 60000) % 60, (action.time / 1000) % 60))
        self.comboBox.setCurrentIndex(self.actionsClasses.index(action.__class__))
        self.settingsButton.setEnabled(self.action.hasSettingsUi)
        self.action = action


class ProgrammingUI(QtGui.QDialog):

    def __init__(self, media, timeMode=True):
        super(ProgrammingUI, self).__init__()

        self.setWindowModality(QtCore.Qt.ApplicationModal)
        self.setMaximumSize(470, 320)
        self.setMinimumSize(470, 320)
        self.resize(470, 320)

        self.timeMode = timeMode
        self.media = media
        self.rows = []

        self.list = QtGui.QListWidget(self)
        self.list.setGeometry(QtCore.QRect(5, 5, 350, 310))
        #self.list.setDragEnabled(True)
        #self.list.setDragDropMode(QtGui.QListWidget.InternalMove)

        self.addButton = QtGui.QPushButton(self)
        self.addButton.setGeometry(360, 10, 100, 30)
        self.addButton.setText("Add")

        self.insertButton = QtGui.QPushButton(self)
        self.insertButton.setGeometry(360, 50, 100, 30)
        self.insertButton.setText("Insert")

        self.removeButton = QtGui.QPushButton(self)
        self.removeButton.setGeometry(360, 90, 100, 30)
        self.removeButton.setText("Remove")

        self.acceptButton = QtGui.QPushButton(self)
        self.acceptButton.setGeometry(360, 140, 100, 30)
        self.acceptButton.setText("Ok")

        self.rejectButton = QtGui.QPushButton(self)
        self.rejectButton.setGeometry(360, 180, 100, 30)
        self.rejectButton.setText("Cancel")

        self.addButton.clicked.connect(lambda: self.add())
        self.insertButton.clicked.connect(lambda: self.insert())
        self.removeButton.clicked.connect(self.remove)
        self.rejectButton.clicked.connect(self.reject)
        self.acceptButton.clicked.connect(self.accept)

    def insert(self, pos=-1, action=None):
        if(pos < 0):
            pos = self.list.currentRow()

        row = Row(self.media, self.timeMode)
        if(action is not None):
            row.setAction(action)
        item = QtGui.QListWidgetItem()
        item.setSizeHint(QtCore.QSize(340, 34))

        self.list.addItem(item)
        self.list.setItemWidget(item, row)

        self.rows.append(row)

    def add(self, action=None):
        self.insert(self.list.count(), action)

    def remove(self):
        self.rows.pop(self.list.currentRow())
        self.list.takeItem(self.list.currentRow())

    def getActions(self):
        return sorted([row.action for row in self.rows], key=PAction.getTime)

    def getProgramStr(self):
        program = ''
        for row in self.rows:
            program += str(row.action) + ';'
        return program[:-1]
        print(program)

    def setActions(self, program):
        for action in program:
            self.add(action)


class Program:

    def __init__(self, actions, media):
        self.media = media
        self.actions = actions

        self.time = MediaTime(media, wait=0.25)
        self.index = 0

        self.time.progress.connect(self.onProgress, QtCore.Qt.QueuedConnection)
        self.proxy = lambda m: self.reset()
        self.media.onStopped.connect(self.proxy)
        self.media.onEndOfStream.connect(self.proxy)
        self.media.onError.connect(self.proxy)

    def onProgress(self, current):
        if(self.index < len(self.actions)):
            if(current >= self.actions[self.index].time):
                self._exec()

    def _exec(self):
        self.actions[self.index].exec_()
        self.index += 1
        if(self.index != len(self.actions)):
            if(self.actions[self.index - 1].time == self.actions[self.index].time):
                self._exec()

    def reset(self):
        self.index = 0

    def setActions(self, actions):
        self.reset()
        self.actions = actions

    def destroy(self):
        self.time.progress.disconnect(self.onProgress)
        self.media.onStopped.disconnect(self.proxy)
        self.media.onEndOfStream.disconnect(self.proxy)
        self.media.onError.disconnect(self.proxy)
